/* TBinderInspector.m
 * Written By:  Thomas Burkholder
 *
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 */

#import "TBinderInspector.h"
#import <objc/objc.h>
#import "TBinder.h"
#import "TBinderListConnectInspector.h"
#import "TDataSourceConnector.h"
#import "SelChooser.h"

@implementation TBinderInspector

- awakeFromNib
{
	fields[0] = interfaceAccessorField;
	fields[1] = interfaceModifierField;
	fields[2] = dataSourceAccessorField;
	fields[3] = dataSourceModifierField;
	return self;
}

- inspectorView
{
	return inspectorView;
}

- inspectBinder:anObject andReturnTo:otherObject
{
	binder = anObject;
	connectInspector = otherObject;
	[self revert:self];
	return self;
}

- choose:sender
{
	int t = [sender tag];
	id binderList =[getTConnectorForDestination([binder interface]) source];
	id src = [getDSConnectorForSource(binderList) destination];
	const char *str;

	str = sel_getName([SelChooser chooseSelectorFor:
					(t/2)?[src class]:[[binder interface] class]
								numArgs:(t%2)
					startWith:sel_getUid([fields[t] stringValue])]);

	[fields[t] setStringValue:str];
	[self ok:self];
	return self;
}

- ok:sender
{

	[binder setInterfaceAccessor:
				sel_getUid([interfaceAccessorField stringValue])];
	[binder setInterfaceModifier:
				sel_getUid([interfaceModifierField stringValue])];
	[binder setDataSourceAccessor:
				sel_getUid([dataSourceAccessorField stringValue])];
	[binder setDataSourceModifier:
				sel_getUid([dataSourceModifierField stringValue])];
	[binder setVerify:[verifySwitch state]];
	[binder setAutoUpdate:[autoUpdateSwitch state]];
	return self;
}

- doneAfterDelay:sender
{
	[self perform:@selector(done:) with:sender afterDelay:0
										cancelPrevious:NO];
	return self;
}

- done:sender
{
	[[inspectorView window] disableFlushWindow];
	[connectInspector revert:self];
	[[connectInspector window] reenableFlushWindow];
	[[connectInspector window] flushWindow];
	return self;
}
- revert:sender
{
	[interfaceAccessorField setStringValue:
			sel_getName([binder interfaceAccessor])];
	[interfaceModifierField setStringValue:
			sel_getName([binder interfaceModifier])];
	[dataSourceAccessorField setStringValue:
			sel_getName([binder dataSourceAccessor])];
	[dataSourceModifierField setStringValue:
			sel_getName([binder dataSourceModifier])];
	[verifySwitch setState:[binder verify]];
	[autoUpdateSwitch setState:[binder autoUpdate]];
	return self;
}

int selectorCheck(const char *method, id object, int args)
{
	BOOL b;
	SEL aSel=sel_getUid(method);
	int actualArgs;

	b =(aSel && sel_isMapped(aSel) && [object respondsTo:aSel]);

	if (!b) return 0;
	
	// remember that any method takes at least 2 args, self and _cmd
	actualArgs = method_getNumberOfArguments(
			class_getInstanceMethod([object class], aSel)) - 2;
	return (b && (args == actualArgs));
}

// text delegate methods
- (BOOL)textWillEnd:sender
{
	id controlEdited = [sender delegate];
	id dest = [binder interface];
	id binderList =[getTConnectorForDestination([binder interface]) source];
	id src = [getDSConnectorForSource(binderList) destination];
	char *str = alloca([sender textLength]+1);
	id testobj;
	int numArgs;

	[sender getSubstring:str start:0 length:[sender textLength]+1];

	if (!dest) return YES;
	if (!src) return NO;

	testobj = ([controlEdited tag] <=1)?dest:src;
	numArgs = ([controlEdited tag]%2);

	if (!selectorCheck(str,testobj,numArgs))
		return YES;

	return NO;
}

@end
